package com.diy.sigmund.diycommon.spring;

import com.diy.sigmund.diycommon.entity.constant.LogConstant;
import com.diy.sigmund.diycommon.spring.config.PortConfig;
import com.diy.sigmund.diycommon.spring.filter.RequestWrapperFilter;
import com.diy.sigmund.diycommon.spring.interceptor.AccessLimitInterceptor;
import com.diy.sigmund.diycommon.spring.interceptor.ApiIdempotentInterceptor;
import com.diy.sigmund.diycommon.spring.interceptor.CustomAsyncHandlerInterceptor;
import com.diy.sigmund.diycommon.spring.interceptor.CustomCallableProcessingInterceptor;
import com.diy.sigmund.diycommon.spring.interceptor.CustomDeferredResultProcessingInterceptor;
import com.diy.sigmund.diycommon.spring.interceptor.MyMethodInterceptor;
import com.diy.sigmund.diycommon.spring.interceptor.SqlInterceptor;
import com.diy.sigmund.diycommon.spring.interceptor.WebInterceptor;
import com.diy.sigmund.diycommon.util.json.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 以java形式定制MVC配置时，实现WebMvcConfigurer接口即可，不要在@Configuration class上标注@EnableWebMvc（因为@EnableWebMvc是默认没有静态资源放行的，即.css .jpg
 * .js等文件默认会被DispatcherServlet以/形式拦截，会导致所有的静态资源css，js等都会被拦截）
 * https://blog.csdn.net/weixin_42950079/article/details/103735290
 * <p>
 * <br>聚合项目只能扫描到当前项目的配置，无法扫描到锁依赖项目的Configuration
 * <br>
 * <br>1、spring boot中通过@Bean声明bean，默认情况下，bean的名称由方法名决定。另外，可以通过@Bean注解里面的name属性主动设置bean的名称。
 * <br>2、通过@Autowired和@Qualifier(“queue-test”)结合使用，可以注入指定名称的bean
 * <br>原文链接：https://blog.csdn.net/w1014074794/article/details/106768607
 *
 * @author ylm-sigmund
 * @since 2021/4/24 21:41
 */
@Configuration
@ConditionalOnProperty(name = "webMvcConfigurer.enable", havingValue = "true", matchIfMissing = true)
public class WebAppConfigurationForCommon implements WebMvcConfigurer {

    @Autowired
    private PortConfig portConfig;
    @Autowired
    private AsyncTaskExecutor asyncTestExecutor;

    private static final Logger log = LoggerFactory.getLogger(WebAppConfigurationForCommon.class);

    // @Bean
    public SqlInterceptor sqlInterceptor() {
        log.info(LogConstant.SPRING_LOG, "SqlInterceptor 初始化完成");
        return new SqlInterceptor();
    }

    //////////
    /////过滤器配置
    //////////

    /**
     * RequestWrapperFilter
     *
     * @return RequestWrapperFilter
     */
    @Bean
    public RequestWrapperFilter requestWrapperFilter() {
        log.info(LogConstant.SPRING_LOG, "requestWrapperFilter 初始化完成");
        return new RequestWrapperFilter();
    }

    /**
     * urlPatterns匹配规则见@see
     *
     * @return FilterRegistrationBean
     * @see RequestWrapperFilter
     */
    @Bean
    public FilterRegistrationBean<RequestWrapperFilter> requestWrapperFilterRegistration() {
        final FilterRegistrationBean<RequestWrapperFilter> filterRegistrationBean =
                new FilterRegistrationBean<>(requestWrapperFilter());
        filterRegistrationBean.setOrder(1);
        // 之所以不用/*是因为有太多的请求，只拦截必要的
        filterRegistrationBean.addUrlPatterns("/common/*", "/user/*", "/order/*");
        log.info(LogConstant.SPRING_LOG, "requestWrapperFilter 注册完成");
        return filterRegistrationBean;
    }

    //////////
    /////拦截器配置
    //////////

    /**
     * 配置WebInterceptor
     *
     * @return WebInterceptor
     */
    @Bean
    public WebInterceptor webInterceptor() {
        log.info(LogConstant.SPRING_LOG, "webInterceptor 初始化完成");
        return new WebInterceptor();
    }

    /**
     * 配置幂等性接口拦截器
     *
     * @return ApiIdempotentInterceptor
     */
    @Bean
    public ApiIdempotentInterceptor apiIdempotentInterceptor() {
        log.info(LogConstant.SPRING_LOG, "apiIdempotentInterceptor 初始化完成");
        return new ApiIdempotentInterceptor();
    }

    /**
     * 访问限流拦截器
     *
     * @return AccessLimitInterceptor
     */
    @Bean
    public AccessLimitInterceptor accessLimitInterceptor() {
        log.info(LogConstant.SPRING_LOG, "accessLimitInterceptor 初始化完成");
        return new AccessLimitInterceptor();
    }

    /**
     * 异步处理拦截器
     */
    @Bean
    public CustomAsyncHandlerInterceptor customAsyncHandlerInterceptor() {
        log.info(LogConstant.SPRING_LOG, "customAsyncHandlerInterceptor 初始化完成");
        return new CustomAsyncHandlerInterceptor();
    }

    public static final String TRACE_EXECUTION = "execution(public * com.diy.sigmund..controller..*(..))";

    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor() {
        MyMethodInterceptor methodInterceptor = new MyMethodInterceptor();
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(TRACE_EXECUTION);

        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(pointcut);
        advisor.setAdvice(methodInterceptor);
        return advisor;
    }

    /**
     * 添加Spring MVC生命周期拦截器，以对MVC进行预处理和后处理
     * 控制器方法调用和资源处理程序请求。
     * 拦截器可以注册以适用于所有请求或受到限制网址格式的子集。
     *
     * @param registry 帮助配置映射的拦截器列表
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        log.info(LogConstant.SPRING_LOG, "[为null表示此时未获取到]本机端口是" + portConfig.getPort());
        log.info(LogConstant.SPRING_LOG, "配置拦截器 开始");
        //指定拦截器要拦截的请求路径(支持*通配符)
        // 注册WebInterceptor
        registry.addInterceptor(webInterceptor()).addPathPatterns("/common/**", "/user/**", "/order/**");
        // 注册幂等性接口拦截器
        registry.addInterceptor(apiIdempotentInterceptor()).addPathPatterns("/common/**", "/user/**", "/order/**");
        // 注册访问限流拦截器
        registry.addInterceptor(accessLimitInterceptor()).addPathPatterns("/common/**", "/user/**", "/order/**");
        // 异步处理拦截器
        registry.addInterceptor(customAsyncHandlerInterceptor()).addPathPatterns("/common/**", "/user/**", "/order/**");

        //注册拦截器2，对超级管理员系统进行权限验证
        //        InterceptorRegistration registration2 = registry.addInterceptor(new SuperAdminInterceptor());
        //        /*指定拦截器2要拦截的请求(支持*通配符)*/
        //        registration2.addPathPatterns("/super_admin/**");
        //        //指定拦截器2不拦截的请求(支持*通配符)
        //        registration2.excludePathPatterns("/super/toLogin");
    }

    /**
     * 配置异步请求处理选项。
     */
    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        log.info(LogConstant.SPRING_LOG, "配置异步请求处理选项 开始");
        // 定异步请求处理超时之前的时间量（以毫秒为单位）。
        configurer.setDefaultTimeout(30_000L);
        configurer.setTaskExecutor(asyncTestExecutor);
        configurer.registerCallableInterceptors(new CustomCallableProcessingInterceptor());
        configurer.registerDeferredResultInterceptors(new CustomDeferredResultProcessingInterceptor());
        WebMvcConfigurer.super.configureAsyncSupport(configurer);
    }

    /**
     * 跨域（当它请求的一个资源是从一个与它本身提供的第一个资源的不同的域名时，一个资源会发起一个跨域HTTP请求(Cross-site HTTP request)。）
     * https://blog.csdn.net/qq_36157687/article/details/103268995
     *
     * @param registry 协助基于全局URL模式的注册
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        log.info("registry:{}", JacksonUtil.toJson(registry));
        //        registry.addMapping("/")//设置允许跨域的路径
        //                .allowedOrigins("*")//设置允许跨域请求的域名
        //                .allowCredentials(true)//是否允许证书 不再默认开启
        //                .allowedMethods("GET", "POST", "PUT", "DELETE")//设置允许的方法
        //                .maxAge(3600);//跨域允许时间
    }

}
